﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Reflection;

namespace DLLToolsLibrary
{

    public partial class DLLViewTree : UserControl
    {
        public event EventHandler<TreeViewEventArgs> SelectionChanged;

        private List<Assembly> assemblies = new List<Assembly>();
        /// <summary>
        /// Assemblies
        /// </summary>
        [Browsable(false)]
        public List<Assembly> Assemblies
        {
            get
            {
                return assemblies;
            }
            set
            {
                assemblies = value;
                ReloadAssemblyTree();
            }
        }


        public DLLViewTree()
        {
            InitializeComponent();
        }


        public DLLViewTree(String assemblyPath)
        {
            ParseAssemblyDir(assemblyPath);
        }


        public void ParseAssemblyDir(String assemblyPath)
        {
            DirectoryInfo assemblyDir = new DirectoryInfo(assemblyPath);

            foreach (FileInfo file in assemblyDir.GetFiles())
            {
                try
                {
                    Assembly assembly = Assembly.LoadFrom(file.FullName);
                    if (!Assemblies.Contains(assembly))
                    {
                        Assemblies.Add(assembly);
                    }
                    else
                    {
                        MessageBox.Show(Properties.Resources.AsmInUseString + ": " + file.Name, Properties.Resources.NoteString, MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
                catch
                {
                    //Keine .NET - DLL, fahre mit nächster DLL fort
                    continue;
                }
            }
            ReloadAssemblyTree();
        }

        public Assembly GetSelectedAssembly()
        {
            AssemblyTreeNode treeNode = (AssemblyTreeNode)treeViewDLLs.SelectedNode;
            return treeNode.Assembly;
        }

        private void ReloadAssemblyTree()
        {
            treeViewDLLs.Nodes.Clear();
            foreach (Assembly assembly in Assemblies)
            {
                XMLDocParser.XMLDocParser assemblyParser = null;
                try
                {
                    assemblyParser = new XMLDocParser.XMLDocParser(assembly.Location.Replace(".dll", ".xml")); //Entsprechende xml - Doku zur assembly laden und parsen
                }
                catch
                {
                    //keine Hilfe vorhanden, weitermachen
                }

                AssemblyName assemblyName = assembly.GetName();
                AssemblyTreeNode assemblyNode = new AssemblyTreeNode(assemblyName.Name, 0, 0);
                assemblyNode.ContextMenuStrip = contextMenuStripAssembly;
                assemblyNode.Assembly = assembly;
                assemblyNode.ToolTipText = assemblyName.FullName;

                if (assemblyParser != null)
                {
                    assemblyNode.Documentation = assemblyParser.ParsedAssembly.Name.Trim(); //einfach: Namen der Assembly in Documentation - Objekt der TreeNode schreiben
                }
                treeViewDLLs.Nodes.Add(assemblyNode);
                foreach (Type t in assembly.GetExportedTypes())
                {
                    AssemblyTreeNode classNode = new AssemblyTreeNode(t.Name);
                    
                    classNode.Assembly = assembly;
                    if (t.IsClass)
                    {
                        classNode.ImageIndex = 1;
                        classNode.SelectedImageIndex = 1;
                    }
                    else if (t.IsInterface)
                    {
                        classNode.ImageIndex = 2;
                        classNode.SelectedImageIndex = 2;
                    }
                    else if (t.IsEnum)
                    {
                        classNode.ImageIndex = 4;
                        classNode.SelectedImageIndex = 4;
                    }
                    
                    if(assemblyParser != null)
                    {
                        try
                        {
                            //etwas schwieriger ;) Klassennamen und Klassenbeschreibung in die Doku hängen. Über Linq lässt sich mit Hilfe des
                            //FullName - Attributs direkt die richtige Klasse heraussuchen. First() wird verwendet, es wird davon ausgegangen, dass nur eine Klasse mit
                            //gleichem FullName existiert.
                            classNode.Documentation = assemblyParser.ParsedAssembly.Classes.Where(c => c.FullName == t.FullName).First().Name.Trim() + "\n" + //Name
                                assemblyParser.ParsedAssembly.Classes.Where(c => c.FullName == t.FullName).First().Description.Trim();                 //Beschreibung

                        }
                        catch
                        {
                            //Klasse nicht gefunden, weitermachen
                        }
                    }

                    assemblyNode.Nodes.Add(classNode);
                    ConstructorInfo[] ctors = t.GetConstructors();

                    if (ctors != null) 
                    {
                        foreach (ConstructorInfo ctor in ctors)
                        {
                            String ctorString = t.Name + "(";
                            foreach (ParameterInfo parameter in ctor.GetParameters())
                            {
                                ctorString += parameter.ParameterType.Name.ToString() + " " + parameter.Name + ", ";
                            }
                            if (ctor.GetParameters() != null && ctor.GetParameters().Length > 0) //ein komma zuviel, wenn parameter vorhanden waren
                            {
                                ctorString = ctorString.Remove(ctorString.Length - 2); //Letztes Komma entfernen
                            }
                            ctorString += ")";

                            AssemblyTreeNode ctorTreeNode = new AssemblyTreeNode(ctorString);
                            ctorTreeNode.Assembly = assembly;
                            ctorTreeNode.ImageIndex = 3;
                            ctorTreeNode.SelectedImageIndex = 3;


                            //TODO: Doku für Konstruktor
                            if (assemblyParser != null)
                            {
                                try
                                {
                                    //Alle Methoden der aktuellen Klasse aus dem Parser holen
                                    List<XMLDocParser.XMLDocParser.Method> constructors = assemblyParser.ParsedAssembly.Classes.Where(c => c.FullName == t.FullName).First().Methods;
                                    //Dann alle Konstruktoren aus dem Parser holen. Vorsicht: Konstruktorüberladung!!! Ergebnis kann mehrzeilig sein!
                                    var ctorsByName = constructors.Where(m => m.Name == "#ctor");

                                    //Für jeden Konstruktor Parameterübereinstimmung überprüfen
                                    foreach (var foundCtor in ctorsByName)
                                    {
                                        List<XMLDocParser.XMLDocParser.Parameter> parameters = foundCtor.Params; //Parameter aus Parser
                                        bool methodCheck = true; //Flag zur Überprüfung, ob Ctor übereinstimmt
                                        ParameterInfo[] ctorParameters = ctor.GetParameters(); //Parameter aus Reflection
                                        for (int i = 0; i < parameters.Count; i++)
                                        {
                                            if (ctorParameters[i].ParameterType != parameters[i].Type) //Wenn Typ nicht übereinstimmt, direkt abbrechen und Flag auf false setzen
                                            {
                                                methodCheck = false;
                                                break;
                                            }
                                        }
                                        if (ctor.GetParameters().Length != parameters.Count) //Nochmal überprüfen ob evtl. der Konstruktor mehr Argumente hat als entsprechender Konstruktor aus Parser.
                                        {
                                            methodCheck = false;
                                        }
                                        if (methodCheck) //Wenn alles gut gegangen ist, Doku erstellen und schnell hier weg! :D
                                        {
                                            ctorTreeNode.Documentation = foundCtor.Name.Trim() + ": " + foundCtor.Description.Trim() + "\n\n";
                                            foreach (var tmpParameter in foundCtor.Params)
                                            {
                                                ctorTreeNode.Documentation += tmpParameter.Name.Trim() + " (" + tmpParameter.Type.ToString() + ")" + ": "
                                                    + tmpParameter.Description.Trim() + "\n";
                                            }
                                            break;
                                        }
                                    }

                                }
                                catch
                                {
                                    //Keine Doku vorhanden, weitermachen
                                }
                            }

                            classNode.Nodes.Add(ctorTreeNode);
                        }
                    }

                    foreach (MethodInfo method in t.GetMethods())
                    {
                        String methodString = method.ReturnType.Name.ToString() + " " +
                            method.Name + "(";
                        foreach (ParameterInfo parameter in method.GetParameters())
                        {
                            methodString += parameter.ParameterType.Name.ToString() + " " + parameter.Name + ", ";
                        }

                        if (method.GetParameters() != null && method.GetParameters().Length > 0) //ein komma zuviel, wenn parameter vorhanden waren
                        {
                            methodString = methodString.Remove(methodString.Length - 2); //Letztes Komma entfernen
                        }
                        methodString += ")";

                        AssemblyTreeNode memberNode = new AssemblyTreeNode(methodString);
                        memberNode.Assembly = assembly;
                        memberNode.ImageIndex = 3;
                        memberNode.SelectedImageIndex = 3;


                        //Parsen der Doku, sehr kompliziert! Nicht am Code rumspielen!!!!!!!!
                        if (assemblyParser != null)
                        {
                            try
                            {
                                //Alle Methoden der aktuellen Klasse aus dem Parser holen
                                List<XMLDocParser.XMLDocParser.Method> methods = assemblyParser.ParsedAssembly.Classes.Where(c => c.FullName == t.FullName).First().Methods;
                                //Dann alle Methoden mit gleichem Namen aus dem Parser holen. Vorsicht: Methodenüberladung!!! Ergebnis kann mehrzeilig sein!
                                var methodsByName = methods.Where(m => m.Name == method.Name);

                                //Für jede Methode Parameterübereinstimmung überprüfen
                                foreach (var foundMethod in methodsByName)
                                {
                                    List<XMLDocParser.XMLDocParser.Parameter> parameters = foundMethod.Params; //Parameter aus Parser
                                    bool methodCheck = true; //Flag zur Überprüfung, ob Methode übereinstimmt
                                    ParameterInfo[] methodParameters = method.GetParameters(); //Parameter aus Reflection
                                    for (int i = 0; i < parameters.Count; i++) 
                                    {
                                        if (methodParameters[i].ParameterType != parameters[i].Type) //Wenn Typ nicht übereinstimmt, direkt abbrechen und Flag auf false setzen
                                        {
                                            methodCheck = false;
                                            break;
                                        }
                                    }
                                    if (method.GetParameters().Length != parameters.Count) //Nochmal überprüfen ob evtl. eine Methode mehr Argumente hat als entsprechende Methode aus Parser.
                                    {
                                        methodCheck = false;
                                    }
                                    if (methodCheck) //Wenn alles gut gegangen ist, Doku erstellen und schnell hier weg! :D
                                    {
                                        memberNode.Documentation = foundMethod.Name.Trim() + ": " + foundMethod.Description.Trim() + "\n\n";
                                        foreach (var tmpParameter in foundMethod.Params) 
                                        {
                                            memberNode.Documentation += tmpParameter.Name.Trim() + " (" + tmpParameter.Type.ToString() + ")" + ": "
                                                + tmpParameter.Description.Trim() + "\n";
                                        }
                                        break;
                                    }
                                }

                            }
                            catch 
                            {
                                //Keine Doku vorhanden, weitermachen
                            }
                        }

                        classNode.Nodes.Add(memberNode);
                    }
                }
            }
        }

        private void DLLViewTree_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Link;
            }
        }

        private void DLLViewTree_DragDrop(object sender, DragEventArgs e)
        {
            String[] files = (String[])e.Data.GetData(DataFormats.FileDrop);
            foreach (String file in files)
            {
                try
                {
                    Assembly assembly = Assembly.LoadFrom(file);
                    if (!Assemblies.Contains(assembly))
                    {
                        Assemblies.Add(assembly);
                    }
                    else
                    {
                        MessageBox.Show(Properties.Resources.AsmInUseString, Properties.Resources.NoteString, MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
                catch
                {
                    //Keine .NET - DLL, fahre mit nächster DLL fort
                    continue;
                }
            }
            ReloadAssemblyTree();
        }

        private void toolStripMenuItemDeleteNode_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show( Properties.Resources.DeleteFront +  (treeViewDLLs.SelectedNode as AssemblyTreeNode).Text + Properties.Resources.DeleteBack, Properties.Resources.DeleteString, MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
            {
                //Löschen veranlassen
                Assemblies.Remove((treeViewDLLs.SelectedNode as AssemblyTreeNode).Assembly);
                (treeViewDLLs.SelectedNode as AssemblyTreeNode).Remove();
            }
        }

        private void treeViewDLLs_MouseUp(object sender, MouseEventArgs e)
        {
            treeViewDLLs.SelectedNode = treeViewDLLs.GetNodeAt(e.Location);
        }


        public class AssemblyTreeNode : TreeNode
        {
            private Assembly assembly;
            public AssemblyTreeNode(String text, int imageIndex, int selectedImageIndex) : base(text, imageIndex, selectedImageIndex) { }
            public AssemblyTreeNode(String text) : base(text) { }
            public String Documentation { get; set; }
            public Assembly Assembly
            {
                get
                {
                    return assembly;
                }
                set
                {
                    assembly = value;
                }
            }
        }

        private void toolStripMenuItemReloadTree_Click(object sender, EventArgs e)
        {
            ReloadAssemblyTree();
        }

        private void toolStripMenuItemGetReferencedAssemblies_Click(object sender, EventArgs e)
        {
            AssemblyName[] referencedAssemblies = (treeViewDLLs.SelectedNode as AssemblyTreeNode).Assembly.GetReferencedAssemblies();
            AssemblyInfoDlg aInfo = new AssemblyInfoDlg(referencedAssemblies);
            aInfo.ShowDialog(this);
            
        }

        private void toolStripMenuItemGetCurrentDomainAssemblies_Click(object sender, EventArgs e)
        {
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (!Assemblies.Contains(assembly))
                {
                    Assemblies.Add(assembly);
                }
                ReloadAssemblyTree();
            }
        }

        private void treeViewDLLs_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (SelectionChanged != null)
            {
                SelectionChanged(this, new TreeViewEventArgs(treeViewDLLs.SelectedNode));
            }
        }
    }

}
